/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package drinkcounter.web.servlet;

import com.csvreader.CsvWriter;
import com.google.common.base.Charsets;
import drinkcounter.util.PartyMarshaller;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import drinkcounter.DrinkCounterService;
import drinkcounter.HistoryService;
import drinkcounter.model.Participant;
import drinkcounter.model.ParticipantHistory;
import drinkcounter.model.PartyHistory;
import drinkcounter.util.PathInfoHelper;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.List;
import javax.cache.Cache;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 *
 * @author Toni
 */
@Singleton
public class PartyAPIServlet extends HttpServlet{

    private static final Logger log = LoggerFactory.getLogger(PartyAPIServlet.class);

    @Inject private DrinkCounterService service;
    @Inject private PartyMarshaller partyMarshaller;
    @Inject private HistoryService historyService;
    @Inject private Cache cache;

    @Override
    public void init() throws ServletException {
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doRequest(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doRequest(req, resp);
    }

    private void doRequest(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String action = getAction(req);
        log.debug("action: "+action);
        if(StringUtils.isBlank(action)){
            printXml(req, resp);
        }else if("add-drink".equals(action)){
            addDrink(req, resp);
        }else if("add-participant".equals(action)){
            addParticipant(req,resp);
        }else if("remove-participant".equals(action)){
            removeParticipant(req, resp);
        }else if("show-history".equals(action)){
            showHistory(req, resp);
        }
        else{
            resp.sendError(404);
        }
        
    }

    private String getAction(HttpServletRequest req){
        String pathInfo = req.getPathInfo();
        String[] parameters = PathInfoHelper.splitPathInfo(pathInfo);
        if(parameters.length < 2){
            return "";
        }else{
            return parameters[1];
        }
        
    }
    private String getPartyId(HttpServletRequest req){
        String pathInfo = req.getPathInfo();
        String[] parameters = PathInfoHelper.splitPathInfo(pathInfo);
        return parameters[0];
    }

    private void printXml(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("application/xml");
        OutputStream out = resp.getOutputStream();
        try {
            String partyId = getPartyId(req);
            if(cache.containsKey("display-"+partyId)){
                IOUtils.write((byte[])cache.get("display-"+partyId), out);
            }else{
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                partyMarshaller.marshall(partyId, baos);
                byte[] bytesXml = baos.toByteArray();
                cache.put("display-"+partyId, bytesXml);
                IOUtils.write(baos.toByteArray(), out);
            }
            
        } catch (Exception ex) {
            log.error("Error building xml response", ex);
        }finally{
            out.close();
        }
    }

    private void addDrink(HttpServletRequest req, HttpServletResponse resp) throws IOException{
        String participantIdentifier = req.getParameter("participant-id");
        service.addDrink(participantIdentifier);
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/plain");
        PrintWriter out = resp.getWriter();
        out.print(service.getDrinks(participantIdentifier).size());
        out.close();
    }

    private void addParticipant(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String partyId = getPartyId(req);

        Participant participant = ParticipantHelper.parseNewParticipant(req);
        participant = service.addParticipant(participant, partyId);
        resp.setContentType("text/plain");
        resp.setCharacterEncoding("UTF-8");
        PrintWriter out = resp.getWriter();
        out.print(participant.getId());
        out.close();
    }

    private void removeParticipant(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String participantId = req.getParameter("participant-id");
        service.deleteParticipant(participantId);
        resp.getOutputStream().close();
    }

    private void showHistory(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/csv");
        resp.setHeader("Content-Disposition", "inline");
        
        PrintWriter out = resp.getWriter();
        try{
            if(cache.containsKey("history-"+getPartyId(req))){
                byte[] cachedHistory = (byte[]) cache.get("history-"+getPartyId(req));
                IOUtils.write(cachedHistory, out);
            }else{
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                CsvWriter csvWriter = new CsvWriter(new OutputStreamWriter(baos, Charsets.UTF_8), ',');
                csvWriter.writeRecord(new String[]{"Time", "Name", "Alcohol Level", "Drinks"});
                List<PartyHistory> partyHistories = historyService.getPartyHistory(getPartyId(req));
                for (PartyHistory partyHistory : partyHistories) {
                    DateTime time = new DateTime(partyHistory.getSnapshotTime());
                    for (ParticipantHistory participantHistory : partyHistory.getParticipants()) {
                        csvWriter.writeRecord(
                                new String[]{
                                    time.toString(),
                                    participantHistory.getParticipantName(),
                                    Float.toString(participantHistory.getAlcoholLevel()),
                                    participantHistory.getTotalDrinks().toString()
                                }
                        );
                    }
                }
                csvWriter.close();
                cache.put("history-"+getPartyId(req), baos.toByteArray());
                IOUtils.write(baos.toByteArray(), out);
            }
        }finally{
            out.close();
        }
    }
}
